package com.grm.workflow.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.grm.common.entity.PageResult;
import com.grm.common.utils.DateUtils;
import com.grm.common.utils.JwtUtil;
import com.grm.workflow.entity.WfProcessStatus;
import com.grm.workflow.enums.ProcessStatusEnum;
import com.grm.workflow.mapper.WfDeployFormMapper;
import com.grm.workflow.mapper.WfProcessStatusMapper;
import com.grm.workflow.request.my.request.WfMyRequestQueryParam;
import com.grm.workflow.response.WfMyRequestResponse;
import com.grm.workflow.service.WfMyRequestService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.Deployment;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WfMyRequestServiceImpl implements WfMyRequestService {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private WfDeployFormMapper wfDeployFormMapper;
    @Autowired
    private JwtUtil jwtUtil;
    @Resource
    private IdentityService identityService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private HistoryService historyService;
    @Resource
    private TaskService taskService;
    @Autowired
    private WfProcessStatusMapper wfProcessStatusMapper;

    @Override
    public PageResult<WfMyRequestResponse> querySelective(Integer pageNum, Integer pageSize, WfMyRequestQueryParam queryParam) {
        // 查询历史流程实例
        String loginUserIdStr = String.valueOf(jwtUtil.getUserId());
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                .startedBy(loginUserIdStr)
                .orderByProcessInstanceStartTime().desc();
        // 应用查询条件
        buildQueryParam(queryParam, historicProcessInstanceQuery);
        long pageTotal = historicProcessInstanceQuery.count();
        if (pageTotal == 0) {
            return new PageResult<>(List.of(), 0L);
        }
        List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery
                .listPage((pageNum - 1) * pageSize, pageSize);
        // 批量查询部署信息、任务和流程状态
        Map<String, Deployment> deploymentMap = fetchDeployments(historicProcessInstances);
        Map<String, List<Task>> taskMap = fetchTasks(historicProcessInstances);
        List<WfProcessStatus> processStatusList = wfProcessStatusMapper.queryProcessStatus("processStatus",
                historicProcessInstances.stream()
                        .map(HistoricProcessInstance::getId).distinct()
                        .collect(Collectors.toList()));
        List<WfMyRequestResponse> responses = new ArrayList<>(historicProcessInstances.size());
        for (HistoricProcessInstance instance : historicProcessInstances) {
            WfMyRequestResponse response = new WfMyRequestResponse();
            response.setProcInsId(instance.getId());
            response.setCreateTime(instance.getStartTime());
            response.setFinishTime(instance.getEndTime());
            response.setProcessStatus(getProcessStatus(processStatusList, instance.getId(), instance.getEndTime()));
            // 计算耗时
            response.setDuration(calculateDuration(instance, instance.getStartTime(), instance.getEndTime()));
            // 填充部署信息
            Deployment deployment = deploymentMap.get(instance.getDeploymentId());
            if (deployment != null) {
                response.setProcDefName(instance.getProcessDefinitionName());
                response.setProcDefVersion(instance.getProcessDefinitionVersion());
                response.setCategory(deployment.getCategory());
            }
            // 填充任务信息
            List<Task> tasks = taskMap.getOrDefault(instance.getId(), List.of());
            if (!tasks.isEmpty()) {
                response.setTaskName(tasks.stream()
                        .map(Task::getName)
                        .filter(StringUtils::isNotBlank)
                        .collect(Collectors.joining(",")));
            }
            responses.add(response);
        }

        return new PageResult<>(responses, pageTotal);
    }

    private String calculateDuration(HistoricProcessInstance historicProcessInstance, Date startTime, Date endTime) {
        if (Objects.nonNull(historicProcessInstance.getEndTime())) {
            return DateUtils.getDatePoor(historicProcessInstance.getEndTime(), historicProcessInstance.getStartTime());
        }
        return DateUtils.getDatePoor(new Date(), historicProcessInstance.getStartTime());
    }

    private void buildQueryParam(WfMyRequestQueryParam queryParam, HistoricProcessInstanceQuery query) {
        if (StringUtils.isNotBlank(queryParam.getProcessKey())) {
            query.processDefinitionKey(queryParam.getProcessKey());
        }
        if (StringUtils.isNotBlank(queryParam.getProcessName())) {
            query.processDefinitionName(queryParam.getProcessName());
        }
        if (StringUtils.isNotBlank(queryParam.getCategory())) {
            query.processDefinitionCategory(queryParam.getCategory());
        }
    }

    private Map<String, Deployment> fetchDeployments(List<HistoricProcessInstance> instances) {
        return repositoryService.createDeploymentQuery()
                .deploymentIds(List.of(instances.stream()
                        .map(HistoricProcessInstance::getDeploymentId).distinct().toArray(String[]::new)))
                .list()
                .stream()
                .collect(Collectors.toMap(Deployment::getId, Function.identity()));
    }

    private Map<String, List<Task>> fetchTasks(List<HistoricProcessInstance> instances) {
        return taskService.createTaskQuery()
                .processInstanceIdIn(List.of(instances.stream()
                        .map(HistoricProcessInstance::getId).distinct().toArray(String[]::new)))
                .list()
                .stream()
                .collect(Collectors.groupingBy(Task::getProcessInstanceId));
    }

    private String getProcessStatus(List<WfProcessStatus> processStatusList, String instanceId, Date instanceEndTime) {
        String processStatus = null;
        for (WfProcessStatus wfProcessStatus : processStatusList) {
            if (wfProcessStatus.getProcInsId().equals(instanceId)) {
                processStatus = wfProcessStatus.getProcessStatus();
                break;
            }
        }
        // 兼容旧流程
        if (processStatus == null) {
            processStatus = ObjectUtil.isNull(instanceEndTime) ? ProcessStatusEnum.RUNNING.getStatus() : ProcessStatusEnum.COMPLETED.getStatus();
        }
        return processStatus;
    }
}
